package twoD.hofem;

/**
 * Creates Basis functions on Ω as tensor product of 1D functions N_k(xi1, xi2)
 * = N_i(xi1, xi2)*N_j(xi1, xi2)
 * 
 * where N_i, N_j are 1D Hierarchic shape functions
 * 
 * @author Team 2
 * 
 */

public class HierarchicFBOnR2ToR implements PolynomialBasisOnR2ToR {

	private class PF implements FunctionR2ToR {

		private FunctionRToR N1_;
		private FunctionRToR N2_;
		private ProductFunctionR2ToR u;
		private int m_;

		private PF(FunctionRToR N1, FunctionRToR N2, int m) {
			N1_ = N1;
			N2_ = N2;
			m_ = m;
			u = new ProductFunctionR2ToR(m_, N1_, N2_);
		}

		public double[] gradientAt(double xi1, double xi2) {
			return u.gradientAt(xi1, xi2);
		}

		public double valueAt(double xi1, double xi2) {
			return u.valueAt(xi1, xi2);
		}

	}

	private int p_ = 1;
	private FunctionR2ToR[] basis_;
	private HierarchicFB b = new HierarchicFB();
	private int[] e = new int[4];

	/**
	 * Constructor
	 * 
	 * @param i
	 *            Polynomial degree
	 */
	public HierarchicFBOnR2ToR(int i) {
		setP(i);
	}

	/**
	 * default Constructor
	 */

	public HierarchicFBOnR2ToR() {
		setP(1);
	}

	public void setP(int p) {
		p_ = p;
		update();
	}

	/**
	 * returns the polynomial degree used for creating the basis.
	 */

	public int getP() {
		return p_;
	}

	/**
	 * returns the basis function in R2 for the given index.
	 */

	public FunctionR2ToR getBasisFunction(int idx) {
		return basis_[idx];
	}

	/**
	 * returns array of the basis functions in R2
	 */

	public FunctionR2ToR[] getBasisFunctions() {
		return basis_;
	}

	/**
	 * returns the dimension of the basis in R2
	 */

	public int getDimension() {
		return basis_.length;
	}

	/**
	 * changes the orientation of shape functions for edge modes with uneven
	 * polynomial degree.
	 * 
	 * +1 means straight and -1 means reverse
	 * 
	 * @param eos
	 */
	public void setEdgesOrientation(int... eos) {

		e = eos;
		update();

	}

	private void update() {

		b.setP(p_);
		FunctionRToR[] N = b.createBasis(p_);

		basis_ = new FunctionR2ToR[(p_ + 1) * (p_ + 1)];

		int cnt = 0;
		// nodal modes
		basis_[cnt++] = new PF(N[0], N[0], 1);
		basis_[cnt++] = new PF(N[1], N[0], 1);
		basis_[cnt++] = new PF(N[1], N[1], 1);
		basis_[cnt++] = new PF(N[0], N[1], 1);

		// edge modes

		for (int j = 2; j <= p_; j++) {
			if ((e[0] < 0) && (j % 2 != 0))
				basis_[cnt++] = new PF(N[j], N[0], -1);
			else
				basis_[cnt++] = new PF(N[j], N[0], 1);
		}

		for (int j = 2; j <= p_; j++) {
			if ((e[1] < 0) && (j % 2 != 0))
				basis_[cnt++] = new PF(N[1], N[j], -1);
			else
				basis_[cnt++] = new PF(N[1], N[j], 1);
		}

		for (int j = 2; j <= p_; j++) {
			if ((e[2] < 0) && (j % 2 != 0))
				basis_[cnt++] = new PF(N[j], N[1], -1);
			else
				basis_[cnt++] = new PF(N[j], N[1], 1);
		}

		for (int j = 2; j <= p_; j++) {
			if ((e[3] < 0) && (j % 2 != 0))
				basis_[cnt++] = new PF(N[0], N[j], -1);
			else
				basis_[cnt++] = new PF(N[0], N[j], 1);
		}

		for (int i = 2; i <= p_; i++) {
			for (int j = 2; j <= p_; j++) {
				{
					basis_[cnt++] = new PF(N[i], N[j], 1);
				}
			}
		}
	}

//	public static void main(String[] args) {
//
//		new PlotFunctionBasisOnR2().setVisible(true);
//
//	}

}
